/* QSyncable Project
   Author: Ben Lau
   License: Apache-2.0
   Web: https://github.com/benlau/qsyncable
*/
#include <QHash>
#include <QDebug>
#include <QLinkedList>
#include "qsdiffrunner.h"
#include "priv/qsdiffrunneralgo_p.h"

/*!
  \class QSDiffRunner
  \inmodule QSyncable

QSDiffRunner compares two QVariantList to produce a patch for transforming one of the list to another list with minimum no. of steps. \
The result can be applied on a QSListModel via QSPatchable interface.
QSDiffRunner use an average O(n) algorithm and therefore it should be fast enough for regular UI application.

*/


QSDiffRunner::QSDiffRunner()
{

}

QString QSDiffRunner::keyField() const
{
    return m_keyField;
}

/*! \fn void QSDiffRunner::setKeyField(const QString &keyField)

  Call this function to set the key field before using compare().
  If it is not set, it won't be able to identify insert, remove and move
  changes.

 */

void QSDiffRunner::setKeyField(const QString &keyField)
{
    m_keyField = keyField;
}

/*! \fn QList<QSChange> QSDiffRunner::compare(const QVariantList &from, const QVariantList &to)

    Call this function to compare two list, then return a
    list of patches required to transform from a list to other with
    the minimum number of steps. It uses an algorithm with average O(n) runtime.

    You should set the key field before calling this function.

    \sa setKeyField

 */

QSPatchSet QSDiffRunner::compare(const QVariantList &from, const QVariantList &to)
{
    QSDiffRunnerAlgo algo;
    algo.setKeyField(m_keyField);
    return algo.compare(from, to);
}

/*! \fn bool QSDiffRunner::patch(QSPatchable *patchable, const QSPatchSet& patches) const

  Call this function to patch a list model that implemented the QSPatchable interface. You should
  use the result generated by QSDiffRunner::compare().

 */


bool QSDiffRunner::patch(QSPatchable *patchable, const QSPatchSet& patches) const
{
    QVariantMap diff;
    foreach (QSPatch patch, patches) {
        switch (patch.type()) {
        case QSPatch::Remove:
            patchable->remove(patch.from(), patch.count());
            break;
        case QSPatch::Insert:
            patchable->insert(patch.from(), patch.data());
            break;
        case QSPatch::Move:
            patchable->move(patch.from(), patch.to(), patch.count());
            break;
        case QSPatch::Update:
            if (patch.data().size() > 0) {
                diff = patch.data().at(0).toMap();
            }
            patchable->set(patch.from(), diff);
            break;
        default:
            break;
        }
    }
    return true;
}

